/*
	File:	video/ModelManager.hpp
	Date:	2013-06-01
	Author:	Asteroth
*/


#ifndef __ZEN_MODEL_MANAGER_HPP__
#define __ZEN_MODEL_MANAGER_HPP__

#include <string>
#include <map>
#include <ptr.hpp>

#include "Model.hpp"

namespace zen {

	namespace video {

		class ModelManager {

		public:

			ModelManager(ShaderManager* materialShaderManager, TextureManager* textureManager) {
				this->materialShaderManager = materialShaderManager;
				this->textureManager = textureManager;
			}

			inline ShaderManager* getMaterialShaderManager() const { return materialShaderManager; }
			inline void setMaterialShaderManager(ShaderManager* materialShaderManager) { this->materialShaderManager = materialShaderManager; }

			inline TextureManager* getTextureManager() const { return textureManager; }
			inline void setTextureManager(TextureManager* textureManager) { this->textureManager = textureManager; }

			/**
			 * Create a model from model definition loaded from file (cache applied).
			 * @return A shared pointer to the created model.
			 */
			inline PModel createModel(const std::string &fileName) {
				model::PDefinition definition = loadDefinition(fileName);
				return createModel(definition);
		
			}

			/**
			 * Create a model from model definition.
			 * @return A shared pointer to the created model.
			 */
			inline PModel createModel(model::PDefinition &definition) {
				if (definition.get() != NULL) {
					PModel model = PModel(new Model(definition));
					return model;
				}
				return PModel();
			}

			/**
			 * Get model definition by id if exists and loaded.
			 * @return The shared pointer to the specified model definition.
			 */
			inline model::PDefinition getDefinition(const std::string &id) {
				std::map<std::string, model::PDefinition>::iterator iter = definitions.find(id);
				if (iter != definitions.end()) {
					return iter->second;
				}
				return model::PDefinition();
			}

			/**
			 * Checks if a model definition with the specified id exists and is loaded.
			 */
			inline bool definitionExists(const std::string &id) {
				std::map<std::string, model::PDefinition>::iterator iter = definitions.find(id);
				if (iter != definitions.end())
					return true;
				return false;
			}

			/**
			 * Insert/replace the model definition for the specified id.
			 */
			inline void setDefinition(const std::string &id, model::PDefinition definition) {
				definitions.insert(std::pair<std::string, model::PDefinition>(id, definition));
			}

			/**
			 * Adds a model definition with a specified id only if there is no other model with the same id.
			 */
			inline void addDefinition(const std::string &id, model::PDefinition definition) {
				std::map<std::string, model::PDefinition>::iterator iter = definitions.find(id);
				if (iter == definitions.end())
					definitions.insert(std::pair<std::string, model::PDefinition>(id, definition));
			}

			/**
			 * Removes all model definitions from the cache, the references used across the application
			 * remain valid.
			 */
			inline void clearDefinitions() {
				definitions.clear();
			}

			/**
			 * Loads a model definition from file, if the definition is already loaded the cached one
			 * will be returned instead of loading again, uses the file name as id.
			 * @param fileName	path to the file to be loaded, this parameter is also used
			 * 					as unique id for the model definition.
			 * @return The shared pointer to the loaded model definition.
			 */
			inline model::PDefinition loadDefinition(const std::string &fileName) {
    			return loadDefinition(fileName, fileName);
			}

			/**
			 * Loads a model definition from file, if the model is already loaded the cached one
			 * will be returned instead of loading again.
			 * @param id		An unique id for the model definition.
			 * @param fileName	path to the file to be loaded.
			 * @return The shared pointer to the loaded model definition.
			 */
			inline model::PDefinition loadDefinition(const std::string &id, const std::string &fileName) {
    			model::PDefinition pdefinition = getDefinition(id);
    			if (pdefinition.get() == NULL) {
					model::Definition* definition = new model::Definition();
					if (definition->loadResource(fileName)) {
						definition->setModelManager(this);
						definition->setMaterialShaderManager(materialShaderManager);
						definition->setTextureManager(textureManager);
    					pdefinition = model::PDefinition(definition);
    					addDefinition(id, pdefinition);
						return pdefinition;
					}
					delete definition;
    			}
    			return pdefinition;
			}

			
		private:

			std::map<std::string, model::PDefinition> definitions;
			TextureManager* textureManager;
			ShaderManager* materialShaderManager;
	
			ModelManager();
			ModelManager(const ModelManager&);
			ModelManager& operator = (const ModelManager&);


		};


	} // namespace video

} // namespace zen

#endif // __ZEN_MODEL_MANAGER_HPP__
